/*     */ package com.zimbra.cs.lmtpserver;
/*     */ 
/*     */ import com.google.common.base.Function;
/*     */ import com.google.common.base.Strings;
/*     */ import com.google.common.collect.Lists;
/*     */ import com.google.common.collect.MapMaker;
/*     */ import com.google.common.collect.Multimap;
/*     */ import com.zimbra.common.account.Key.AccountBy;
/*     */ import com.zimbra.common.lmtp.LmtpClient;
/*     */ import com.zimbra.common.lmtp.LmtpProtocolException;
/*     */ import com.zimbra.common.localconfig.DebugConfig;
/*     */ import com.zimbra.common.localconfig.KnownKey;
/*     */ import com.zimbra.common.localconfig.LC;
/*     */ import com.zimbra.common.mime.Rfc822ValidationInputStream;
/*     */ import com.zimbra.common.service.ServiceException;
/*     */ import com.zimbra.common.util.BufferStream;
/*     */ import com.zimbra.common.util.ByteUtil;
/*     */ import com.zimbra.common.util.CopyInputStream;
/*     */ import com.zimbra.common.util.Log;
/*     */ import com.zimbra.common.util.LruMap;
/*     */ import com.zimbra.common.util.MapUtil;
/*     */ import com.zimbra.common.util.TimeoutMap;
/*     */ import com.zimbra.common.util.ZimbraLog;
/*     */ import com.zimbra.cs.account.Account;
/*     */ import com.zimbra.cs.account.Config;
/*     */ import com.zimbra.cs.account.Provisioning;
/*     */ import com.zimbra.cs.account.Server;
/*     */ import com.zimbra.cs.filter.RuleManager;
/*     */ import com.zimbra.cs.mailbox.DeliveryContext;
/*     */ import com.zimbra.cs.mailbox.DeliveryOptions;
/*     */ import com.zimbra.cs.mailbox.Flag;
/*     */ import com.zimbra.cs.mailbox.Folder;
/*     */ import com.zimbra.cs.mailbox.Folder.FolderOptions;
/*     */ import com.zimbra.cs.mailbox.MailItem.Type;
/*     */ import com.zimbra.cs.mailbox.Mailbox;
/*     */ import com.zimbra.cs.mailbox.MailboxManager;
/*     */ import com.zimbra.cs.mailbox.Message;
/*     */ import com.zimbra.cs.mailbox.MessageCache;
/*     */ import com.zimbra.cs.mailbox.Notification;
/*     */ import com.zimbra.cs.mailbox.QuotaWarning;
/*     */ import com.zimbra.cs.mime.ParsedMessage;
/*     */ import com.zimbra.cs.mime.ParsedMessageOptions;
/*     */ import com.zimbra.cs.service.util.ItemId;
/*     */ import com.zimbra.cs.store.Blob;
/*     */ import com.zimbra.cs.store.BlobInputStream;
/*     */ import com.zimbra.cs.store.MailboxBlob;
/*     */ import com.zimbra.cs.store.StoreManager;
/*     */ import com.zimbra.cs.util.Zimbra;
/*     */ import java.io.ByteArrayInputStream;
/*     */ import java.io.File;
/*     */ import java.io.IOException;
/*     */ import java.io.InputStream;
/*     */ import java.util.ArrayList;
/*     */ import java.util.Collection;
/*     */ import java.util.HashMap;
/*     */ import java.util.HashSet;
/*     */ import java.util.Iterator;
/*     */ import java.util.List;
/*     */ import java.util.Map;
/*     */ import java.util.Set;
/*     */ import java.util.concurrent.CopyOnWriteArrayList;
/*     */ import java.util.concurrent.TimeUnit;
/*     */ import java.util.concurrent.locks.ReentrantLock;
/*     */ import javax.mail.MessagingException;
/*     */ import javax.mail.internet.MimeMessage;
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ public class ZimbraLmtpBackend
/*     */   implements LmtpBackend
/*     */ {
/*  84 */   private static List<LmtpCallback> callbacks = new CopyOnWriteArrayList();
/*     */   private static Map<String, Set<Integer>> receivedMessageIDs;
/*  86 */   private static final Map<Integer, ReentrantLock> mailboxDeliveryLocks = createMailboxDeliveryLocks();
/*     */   private final LmtpConfig config;
/*     */   
/*     */   public ZimbraLmtpBackend(LmtpConfig lmtpConfig)
/*     */   {
/*  91 */     this.config = lmtpConfig;
/*  92 */     checkDedupeCacheSize();
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */ 
/*     */   public static void addCallback(LmtpCallback callback)
/*     */   {
/* 100 */     if (callback == null) {
/* 101 */       ZimbraLog.lmtp.error("", new IllegalStateException("LmtpCallback cannot be null"));
/* 102 */       return;
/*     */     }
/* 104 */     ZimbraLog.lmtp.info("Adding LMTP callback: %s", new Object[] { callback.getClass().getName() });
/* 105 */     callbacks.add(callback);
/*     */   }
/*     */   
/*     */   static {
/* 109 */     addCallback(Notification.getInstance());
/* 110 */     addCallback(QuotaWarning.getInstance());
/*     */   }
/*     */   
/*     */   private static Map<Integer, ReentrantLock> createMailboxDeliveryLocks() {
/* 114 */     Function<Integer, ReentrantLock> lockCreator = new Function()
/*     */     {
/*     */       public ReentrantLock apply(Integer from) {
/* 117 */         return new ReentrantLock();
/*     */       }
/* 119 */     };
/* 120 */     return new MapMaker().makeComputingMap(lockCreator);
/*     */   }
/*     */   
/*     */   public LmtpReply getAddressStatus(LmtpAddress address) {
/* 124 */     String addr = address.getEmailAddress();
/*     */     try
/*     */     {
/* 127 */       Provisioning prov = Provisioning.getInstance();
/* 128 */       Account acct = prov.get(Key.AccountBy.name, addr);
/* 129 */       if (acct == null) {
/* 130 */         ZimbraLog.lmtp.info("rejecting address " + addr + ": no account");
/* 131 */         return LmtpReply.NO_SUCH_USER;
/*     */       }
/*     */       
/* 134 */       String acctStatus = acct.getAccountStatus(prov);
/* 135 */       if (acctStatus == null) {
/* 136 */         ZimbraLog.lmtp.warn("rejecting address " + addr + ": no account status");
/* 137 */         return LmtpReply.NO_SUCH_USER;
/*     */       }
/*     */       
/* 140 */       if (acctStatus.equals("maintenance")) {
/* 141 */         ZimbraLog.lmtp.info("try again for address " + addr + ": account status maintenance");
/* 142 */         return LmtpReply.MAILBOX_DISABLED;
/*     */       }
/*     */       
/* 145 */       if (Provisioning.onLocalServer(acct)) {
/* 146 */         address.setOnLocalServer(true);
/* 147 */       } else if (Provisioning.getInstance().getServer(acct) != null) {
/* 148 */         address.setOnLocalServer(false);
/* 149 */         address.setRemoteServer(acct.getMailHost());
/*     */       } else {
/* 151 */         ZimbraLog.lmtp.warn("try again for address " + addr + ": mailbox is not on this server");
/* 152 */         return LmtpReply.MAILBOX_NOT_ON_THIS_SERVER;
/*     */       }
/*     */       
/* 155 */       if (acctStatus.equals("pending")) {
/* 156 */         ZimbraLog.lmtp.info("rejecting address " + addr + ": account status pending");
/* 157 */         return LmtpReply.NO_SUCH_USER;
/*     */       }
/*     */       
/* 160 */       if (acctStatus.equals("closed")) {
/* 161 */         ZimbraLog.lmtp.info("rejecting address " + addr + ": account status closed");
/* 162 */         return LmtpReply.NO_SUCH_USER;
/*     */       }
/*     */       
/* 165 */       if ((acctStatus.equals("active")) || (acctStatus.equals("lockout")) || (acctStatus.equals("locked")))
/*     */       {
/*     */ 
/*     */ 
/* 169 */         return LmtpReply.RECIPIENT_OK;
/*     */       }
/*     */       
/* 172 */       ZimbraLog.lmtp.info("rejecting address " + addr + ": unknown account status " + acctStatus);
/* 173 */       return LmtpReply.NO_SUCH_USER;
/*     */     }
/*     */     catch (ServiceException e) {
/* 176 */       if (e.isReceiversFault()) {
/* 177 */         ZimbraLog.lmtp.warn("try again for address " + addr + ": exception occurred", e);
/* 178 */         return LmtpReply.MAILBOX_DISABLED;
/*     */       }
/* 180 */       ZimbraLog.lmtp.warn("rejecting address " + addr + ": exception occurred", e); }
/* 181 */     return LmtpReply.NO_SUCH_USER;
/*     */   }
/*     */   
/*     */ 
/*     */   private boolean dedupe(ParsedMessage pm, Mailbox mbox)
/*     */     throws ServiceException
/*     */   {
/* 188 */     if ((pm == null) || (mbox == null) || (!mbox.getAccount().isPrefMessageIdDedupingEnabled())) {
/* 189 */       return false;
/*     */     }
/* 191 */     checkDedupeCacheSize();
/* 192 */     String msgid = getMessageID(pm);
/* 193 */     if ((msgid == null) || (msgid.equals(""))) {
/* 194 */       return false;
/*     */     }
/* 196 */     synchronized (ZimbraLmtpBackend.class) {
/* 197 */       Set<Integer> mboxIds = (Set)receivedMessageIDs.get(msgid);
/* 198 */       if ((mboxIds != null) && (mboxIds.contains(Integer.valueOf(mbox.getId())))) {
/* 199 */         return true;
/*     */       }
/*     */     }
/* 202 */     return false;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   private String getMessageID(ParsedMessage pm)
/*     */   {
/*     */     try
/*     */     {
/* 211 */       String id = pm.getMimeMessage().getHeader("Resent-Message-ID", null);
/* 212 */       if (!Strings.isNullOrEmpty(id)) {
/* 213 */         ZimbraLog.lmtp.debug("Resent-Message-ID=%s", new Object[] { id });
/* 214 */         return id;
/*     */       }
/*     */     } catch (MessagingException e) {
/* 217 */       ZimbraLog.lmtp.warn("Unable to determine Resent-Message-ID header value", e);
/*     */     }
/* 219 */     String id = pm.getMessageID();
/* 220 */     ZimbraLog.lmtp.debug("Resent-Message-ID not found.  Message-ID=%s", new Object[] { id });
/* 221 */     return id;
/*     */   }
/*     */   
/*     */ 
/*     */ 
/*     */   private void checkDedupeCacheSize()
/*     */   {
/*     */     try
/*     */     {
/* 230 */       Config config = Provisioning.getInstance().getConfig();
/* 231 */       int cacheSize = config.getMessageIdDedupeCacheSize();
/* 232 */       long entryTimeout = config.getMessageIdDedupeCacheTimeout();
/* 233 */       synchronized (ZimbraLmtpBackend.class) {
/* 234 */         Map<String, Set<Integer>> newMap = null;
/* 235 */         if (receivedMessageIDs == null)
/*     */         {
/* 237 */           receivedMessageIDs = entryTimeout == 0L ? new LruMap(cacheSize) : new TimeoutMap(entryTimeout);
/*     */ 
/*     */         }
/* 240 */         else if ((receivedMessageIDs instanceof LruMap)) {
/* 241 */           if (entryTimeout != 0L)
/*     */           {
/* 243 */             newMap = MapUtil.newTimeoutMap(entryTimeout);
/* 244 */           } else if (((LruMap)receivedMessageIDs).getMaxSize() != cacheSize)
/*     */           {
/* 246 */             newMap = MapUtil.newLruMap(cacheSize);
/*     */           }
/* 248 */         } else if ((receivedMessageIDs instanceof TimeoutMap)) {
/* 249 */           if (entryTimeout == 0L)
/*     */           {
/* 251 */             newMap = MapUtil.newLruMap(cacheSize);
/*     */           } else {
/* 253 */             ((TimeoutMap)receivedMessageIDs).setTimeout(entryTimeout);
/*     */           }
/*     */         }
/* 256 */         if (newMap != null)
/*     */         {
/*     */ 
/*     */ 
/* 260 */           newMap.putAll(receivedMessageIDs);
/* 261 */           receivedMessageIDs = newMap;
/*     */         }
/*     */       }
/*     */     } catch (ServiceException e) {
/* 265 */       ZimbraLog.lmtp.warn("Unable to update dedupe cache size.", e);
/*     */       
/* 267 */       synchronized (ZimbraLmtpBackend.class) {
/* 268 */         if (receivedMessageIDs == null) {
/* 269 */           receivedMessageIDs = new LruMap(0);
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void addToDedupeCache(ParsedMessage pm, Mailbox mbox) {
/* 276 */     if ((pm == null) || (mbox == null))
/* 277 */       return;
/* 278 */     String msgid = getMessageID(pm);
/* 279 */     if ((msgid == null) || (msgid.equals(""))) {
/* 280 */       return;
/*     */     }
/* 282 */     synchronized (ZimbraLmtpBackend.class) {
/* 283 */       Set<Integer> mboxIds = (Set)receivedMessageIDs.get(msgid);
/* 284 */       if (mboxIds == null) {
/* 285 */         mboxIds = new HashSet();
/* 286 */         receivedMessageIDs.put(msgid, mboxIds);
/*     */       }
/* 288 */       mboxIds.add(Integer.valueOf(mbox.getId()));
/*     */     }
/*     */   }
/*     */   
/*     */   private void removeFromDedupeCache(String msgid, Mailbox mbox) {
/* 293 */     if ((mbox == null) || (Strings.isNullOrEmpty(msgid))) {
/* 294 */       return;
/*     */     }
/* 296 */     synchronized (ZimbraLmtpBackend.class) {
/* 297 */       Set<Integer> mboxIds = (Set)receivedMessageIDs.get(msgid);
/* 298 */       if (mboxIds != null) {
/* 299 */         mboxIds.remove(Integer.valueOf(mbox.getId()));
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static enum DeliveryAction {
/* 305 */     discard, 
/* 306 */     defer, 
/* 307 */     deliver;
/*     */     
/*     */     private DeliveryAction() {}
/*     */   }
/*     */   
/*     */   private static class RecipientDetail { public Account account;
/*     */     public Mailbox mbox;
/*     */     public ParsedMessage pm;
/*     */     public boolean esd;
/*     */     public ZimbraLmtpBackend.DeliveryAction action;
/*     */     
/* 318 */     public RecipientDetail(Account a, Mailbox m, ParsedMessage p, boolean endSharedDelivery, ZimbraLmtpBackend.DeliveryAction da) { this.account = a;
/* 319 */       this.mbox = m;
/* 320 */       this.pm = p;
/* 321 */       this.esd = endSharedDelivery;
/* 322 */       this.action = da;
/*     */     }
/*     */   }
/*     */   
/*     */   public void deliver(LmtpEnvelope env, InputStream in, int sizeHint) throws UnrecoverableLmtpException
/*     */   {
/* 328 */     CopyInputStream cis = null;
/* 329 */     Blob blob = null;
/*     */     try {
/* 331 */       int bufLen = Provisioning.getInstance().getLocalServer().getMailDiskStreamingThreshold();
/* 332 */       cis = new CopyInputStream(in, sizeHint, bufLen, bufLen);
/* 333 */       in = cis;
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 341 */       Rfc822ValidationInputStream validator = null;
/* 342 */       if (LC.zimbra_lmtp_validate_messages.booleanValue()) {
/* 343 */         validator = new Rfc822ValidationInputStream(in, LC.zimbra_lmtp_max_line_length.longValue());
/* 344 */         in = validator;
/*     */       }
/*     */       try
/*     */       {
/* 348 */         blob = StoreManager.getInstance().storeIncoming(in);
/*     */       } catch (IOException ioe) {
/* 350 */         throw new UnrecoverableLmtpException("Error in storing incoming message", ioe);
/*     */       }
/*     */       
/* 353 */       if ((validator != null) && (!validator.isValid())) {
/*     */         try {
/* 355 */           StoreManager.getInstance().delete(blob);
/*     */         } catch (IOException e) {
/* 357 */           ZimbraLog.lmtp.warn("Error in deleting blob %s", blob, e);
/*     */         }
/* 359 */         setDeliveryStatuses(env.getRecipients(), LmtpReply.INVALID_BODY_PARAMETER);
/*     */       }
/*     */       else
/*     */       {
/* 363 */         BufferStream bs = cis.getBufferStream();
/* 364 */         byte[] data = bs.isPartial() ? null : bs.getBuffer();
/*     */         
/* 366 */         BlobInputStream bis = null;
/* 367 */         MimeMessage mm = null;
/*     */         
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */         try
/*     */         {
/* 384 */           deliverMessageToLocalMailboxes(blob, bis, data, mm, env);
/*     */         } catch (Exception e) {
/* 386 */           ZimbraLog.lmtp.warn("Exception delivering mail (temporary failure)", e);
/* 387 */           setDeliveryStatuses(env.getLocalRecipients(), LmtpReply.TEMPORARY_FAILURE);
/*     */         }
/*     */         try
/*     */         {
/* 391 */           deliverMessageToRemoteMailboxes(blob, data, env);
/*     */         } catch (Exception e) {
/* 393 */           ZimbraLog.lmtp.warn("Exception delivering remote mail", e);
/* 394 */           setDeliveryStatuses(env.getRemoteRecipients(), LmtpReply.TEMPORARY_FAILURE);
/*     */         }
/*     */       }
/*     */       return; } catch (ServiceException e) { ZimbraLog.lmtp.warn("Exception delivering mail (temporary failure)", e);
/* 398 */       setDeliveryStatuses(env.getRecipients(), LmtpReply.TEMPORARY_FAILURE);
/*     */     } finally {
/* 400 */       if (cis != null) {
/* 401 */         cis.release();
/*     */       }
/*     */       
/* 404 */       if (blob != null) {
/*     */         try
/*     */         {
/* 407 */           StoreManager.getInstance().delete(blob);
/*     */         } catch (IOException e) {
/* 409 */           ZimbraLog.lmtp.warn("Error in deleting blob %s", blob, e);
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void deliverMessageToLocalMailboxes(Blob blob, BlobInputStream bis, byte[] data, MimeMessage mm, LmtpEnvelope env)
/*     */     throws ServiceException, IOException
/*     */   {
/* 418 */     List<LmtpAddress> recipients = env.getLocalRecipients();
/* 419 */     String envSender = env.getSender().getEmailAddress();
/*     */     
/* 421 */     boolean shared = recipients.size() > 1;
/* 422 */     List<Integer> targetMailboxIds = new ArrayList(recipients.size());
/*     */     
/* 424 */     Map<LmtpAddress, RecipientDetail> rcptMap = new HashMap(recipients.size());
/*     */     
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/*     */     try
/*     */     {
/* 432 */       ParsedMessage pmAttachIndex = null;
/*     */       
/* 434 */       ParsedMessage pmNoAttachIndex = null;
/*     */       
/*     */ 
/* 437 */       String msgId = null;
/*     */       
/* 439 */       for (LmtpAddress recipient : recipients) {
/* 440 */         String rcptEmail = recipient.getEmailAddress();
/*     */         Account account;
/*     */         Mailbox mbox;
/*     */         boolean attachmentsIndexingEnabled;
/*     */         try
/*     */         {
/* 446 */           account = Provisioning.getInstance().get(Key.AccountBy.name, rcptEmail);
/* 447 */           if (account == null) {
/* 448 */             ZimbraLog.mailbox.warn("No account found delivering mail to " + rcptEmail);
/* 449 */             continue;
/*     */           }
/* 451 */           mbox = MailboxManager.getInstance().getMailboxByAccount(account);
/* 452 */           if (mbox == null) {
/* 453 */             ZimbraLog.mailbox.warn("No mailbox found delivering mail to " + rcptEmail);
/* 454 */             continue;
/*     */           }
/* 456 */           attachmentsIndexingEnabled = mbox.attachmentsIndexingEnabled();
/*     */         } catch (ServiceException se) {
/* 458 */           if (se.isReceiversFault()) {
/* 459 */             ZimbraLog.mailbox.info("Recoverable exception getting mailbox for " + rcptEmail, se);
/* 460 */             rcptMap.put(recipient, new RecipientDetail(null, null, null, false, DeliveryAction.defer));
/*     */           } else {
/* 462 */             ZimbraLog.mailbox.warn("Unrecoverable exception getting mailbox for " + rcptEmail, se);
/*     */           } }
/* 464 */         continue;
/*     */         
/*     */ 
/* 467 */         if ((account != null) && (mbox != null)) { ParsedMessageOptions pmo;
/*     */           ParsedMessageOptions pmo;
/* 469 */           if (mm != null) {
/* 470 */             pmo = new ParsedMessageOptions().setContent(mm).setDigest(blob.getDigest()).setSize(blob.getRawSize());
/*     */           } else {
/* 472 */             pmo = new ParsedMessageOptions(blob, data);
/*     */           }
/*     */           ParsedMessage pm;
/*     */           ParsedMessage pm;
/* 476 */           if (attachmentsIndexingEnabled) {
/* 477 */             if (pmAttachIndex == null) {
/* 478 */               pmo.setAttachmentIndexing(true);
/* 479 */               ZimbraLog.lmtp.debug("Creating ParsedMessage from %s with attachment indexing enabled", new Object[] { data == null ? "file" : "memory" });
/* 480 */               pmAttachIndex = new ParsedMessage(pmo);
/*     */             }
/* 482 */             pm = pmAttachIndex;
/*     */           } else {
/* 484 */             if (pmNoAttachIndex == null) {
/* 485 */               pmo.setAttachmentIndexing(false);
/* 486 */               ZimbraLog.lmtp.debug("Creating ParsedMessage from %s with attachment indexing disabled", new Object[] { data == null ? "file" : "memory" });
/* 487 */               pmNoAttachIndex = new ParsedMessage(pmo);
/*     */             }
/* 489 */             pm = pmNoAttachIndex;
/*     */           }
/*     */           
/* 492 */           msgId = pm.getMessageID();
/*     */           
/* 494 */           if (account.isPrefMailLocalDeliveryDisabled()) {
/* 495 */             ZimbraLog.lmtp.debug("Local delivery disabled for account %s", new Object[] { rcptEmail });
/* 496 */             rcptMap.put(recipient, new RecipientDetail(account, mbox, pm, false, DeliveryAction.discard));
/*     */ 
/*     */           }
/*     */           else
/*     */           {
/*     */ 
/* 502 */             DeliveryAction da = DeliveryAction.deliver;
/* 503 */             boolean endSharedDelivery = false;
/* 504 */             if (shared) {
/* 505 */               if (mbox.beginSharedDelivery()) {
/* 506 */                 endSharedDelivery = true;
/*     */               }
/*     */               else {
/* 509 */                 da = DeliveryAction.defer;
/*     */               }
/*     */             }
/* 512 */             rcptMap.put(recipient, new RecipientDetail(account, mbox, pm, endSharedDelivery, da));
/* 513 */             if (da == DeliveryAction.deliver) {
/* 514 */               targetMailboxIds.add(Integer.valueOf(mbox.getId()));
/*     */             }
/*     */           }
/*     */         }
/*     */       }
/* 519 */       ZimbraLog.removeAccountFromContext();
/* 520 */       if (ZimbraLog.lmtp.isInfoEnabled()) {
/* 521 */         ZimbraLog.lmtp.info("Delivering message: size=%s, nrcpts=%d, sender=%s, msgid=%s", new Object[] { Integer.toString(env.getSize()) + " bytes", Integer.valueOf(recipients.size()), env.getSender(), msgId == null ? "" : msgId });
/*     */       }
/*     */       
/*     */ 
/*     */ 
/*     */ 
/*     */ 
/* 528 */       DeliveryContext sharedDeliveryCtxt = new DeliveryContext(shared, targetMailboxIds);
/* 529 */       sharedDeliveryCtxt.setIncomingBlob(blob);
/*     */       
/*     */ 
/*     */ 
/* 533 */       for (LmtpAddress recipient : recipients) {
/* 534 */         String rcptEmail = recipient.getEmailAddress();
/* 535 */         LmtpReply reply = LmtpReply.TEMPORARY_FAILURE;
/* 536 */         RecipientDetail rd = (RecipientDetail)rcptMap.get(recipient);
/* 537 */         if (rd.account != null)
/* 538 */           ZimbraLog.addAccountNameToContext(rd.account.getName());
/* 539 */         if (rd.mbox != null) {
/* 540 */           ZimbraLog.addMboxToContext(rd.mbox.getId());
/*     */         }
/* 542 */         boolean success = false;
/*     */         try {
/* 544 */           if (rd != null) {
/* 545 */             switch (rd.action) {
/*     */             case discard: 
/* 547 */               ZimbraLog.lmtp.info("accepted and discarded message from=%s,to=%s: local delivery is disabled", new Object[] { envSender, rcptEmail });
/*     */               
/* 549 */               if (rd.account.getPrefMailForwardingAddress() != null)
/*     */               {
/* 551 */                 for (LmtpCallback callback : callbacks) {
/* 552 */                   ZimbraLog.lmtp.debug("Executing callback %s", new Object[] { callback.getClass().getName() });
/* 553 */                   callback.forwardWithoutDelivery(rd.account, rd.mbox, envSender, rcptEmail, rd.pm);
/*     */                 }
/*     */               }
/* 556 */               reply = LmtpReply.DELIVERY_OK;
/* 557 */               break;
/*     */             case deliver: 
/* 559 */               Account account = rd.account;
/* 560 */               Mailbox mbox = rd.mbox;
/* 561 */               ParsedMessage pm = rd.pm;
/* 562 */               List<ItemId> addedMessageIds = null;
/* 563 */               ReentrantLock lock = (ReentrantLock)mailboxDeliveryLocks.get(Integer.valueOf(mbox.getId()));
/*     */               boolean acquiredLock;
/*     */               try
/*     */               {
/* 567 */                 acquiredLock = lock.tryLock(LC.zimbra_mailbox_lock_timeout.intValue(), TimeUnit.SECONDS);
/*     */               } catch (InterruptedException e) {
/* 569 */                 acquiredLock = false;
/*     */               }
/* 571 */               if (!acquiredLock) {
/* 572 */                 ZimbraLog.lmtp.info("try again for message from=%s,to=%s: another mail delivery in progress.", new Object[] { envSender, rcptEmail });
/*     */                 
/* 574 */                 reply = LmtpReply.TEMPORARY_FAILURE;
/*     */               }
/*     */               else {
/*     */                 try {
/* 578 */                   if (dedupe(pm, mbox))
/*     */                   {
/* 580 */                     ZimbraLog.lmtp.info("Not delivering message with duplicate Message-ID %s", new Object[] { pm.getMessageID() });
/* 581 */                   } else if (mbox.dedupeForSelfMsg(pm)) {
/* 582 */                     ZimbraLog.mailbox.info("not delivering message, because it is a duplicate of sent message %s", new Object[] { pm.getMessageID() });
/*     */ 
/*     */                   }
/* 585 */                   else if (recipient.getSkipFilters()) {
/* 586 */                     msgId = pm.getMessageID();
/* 587 */                     int folderId = 2;
/* 588 */                     if (recipient.getFolder() != null) {
/*     */                       try {
/* 590 */                         Folder folder = mbox.getFolderByPath(null, recipient.getFolder());
/* 591 */                         folderId = folder.getId();
/*     */                       } catch (ServiceException se) {
/* 593 */                         if (se.getCode().equals("mail.NO_SUCH_FOLDER")) {
/* 594 */                           Folder folder = mbox.createFolder(null, recipient.getFolder(), new Folder.FolderOptions().setDefaultView(MailItem.Type.MESSAGE));
/*     */                           
/* 596 */                           folderId = folder.getId();
/*     */                         } else {
/* 598 */                           throw se;
/*     */                         }
/*     */                       }
/*     */                     }
/* 602 */                     int flags = Flag.BITMASK_UNREAD;
/* 603 */                     if (recipient.getFlags() != null) {
/* 604 */                       flags = Flag.toBitmask(recipient.getFlags());
/*     */                     }
/* 606 */                     DeliveryOptions dopt = new DeliveryOptions().setFolderId(folderId);
/* 607 */                     dopt.setFlags(flags).setTags(recipient.getTags()).setRecipientEmail(rcptEmail);
/* 608 */                     Message msg = mbox.addMessage(null, pm, dopt, sharedDeliveryCtxt);
/* 609 */                     addedMessageIds = Lists.newArrayList(new ItemId[] { new ItemId(msg) });
/* 610 */                   } else if (!DebugConfig.disableIncomingFilter)
/*     */                   {
/*     */ 
/* 613 */                     pm.getMessageID();
/* 614 */                     addedMessageIds = RuleManager.applyRulesToIncomingMessage(null, mbox, pm, (int)blob.getRawSize(), rcptEmail, sharedDeliveryCtxt, 2, false);
/*     */                   }
/*     */                   else
/*     */                   {
/* 618 */                     pm.getMessageID();
/* 619 */                     DeliveryOptions dopt = new DeliveryOptions().setFolderId(2);
/* 620 */                     dopt.setFlags(Flag.BITMASK_UNREAD).setRecipientEmail(rcptEmail);
/* 621 */                     Message msg = mbox.addMessage(null, pm, dopt, sharedDeliveryCtxt);
/* 622 */                     addedMessageIds = Lists.newArrayList(new ItemId[] { new ItemId(msg) });
/*     */                   }
/* 624 */                   success = true;
/* 625 */                   if ((addedMessageIds != null) && (addedMessageIds.size() > 0)) {
/* 626 */                     addToDedupeCache(pm, mbox);
/*     */                   }
/*     */                 } finally {
/* 629 */                   lock.unlock();
/*     */                 }
/*     */                 Iterator i$;
/* 632 */                 if ((addedMessageIds != null) && (addedMessageIds.size() > 0))
/*     */                 {
/* 634 */                   for (i$ = callbacks.iterator(); i$.hasNext();) { callback = (LmtpCallback)i$.next();
/* 635 */                     for (ItemId id : addedMessageIds) {
/* 636 */                       if (id.belongsTo(mbox))
/*     */                       {
/* 638 */                         ZimbraLog.lmtp.debug("Executing callback %s", new Object[] { callback.getClass().getName() });
/*     */                         try {
/* 640 */                           Message msg = mbox.getMessageById(null, id.getId());
/* 641 */                           callback.afterDelivery(account, mbox, envSender, rcptEmail, msg);
/*     */                         } catch (Throwable t) {
/* 643 */                           if ((t instanceof OutOfMemoryError)) {
/* 644 */                             Zimbra.halt("LMTP callback failed", t);
/*     */                           } else
/* 646 */                             ZimbraLog.lmtp.warn("LMTP callback threw an exception", t);
/*     */                         }
/*     */                       }
/*     */                     }
/*     */                   }
/*     */                 }
/*     */                 LmtpCallback callback;
/* 653 */                 reply = LmtpReply.DELIVERY_OK; }
/* 654 */               break;
/*     */             
/*     */ 
/*     */ 
/*     */             case defer: 
/* 659 */               ZimbraLog.lmtp.info("try again for message from=%s,to=%s: mailbox skipped", new Object[] { envSender, rcptEmail });
/*     */               
/* 661 */               reply = LmtpReply.TEMPORARY_FAILURE;
/*     */             }
/*     */           }
/*     */           else
/*     */           {
/* 666 */             ZimbraLog.lmtp.info("rejecting message from=%s,to=%s: account or mailbox not found", new Object[] { envSender, rcptEmail });
/*     */             
/* 668 */             reply = LmtpReply.PERMANENT_FAILURE;
/*     */           }
/*     */         } catch (ServiceException e) {
/* 671 */           if (e.getCode().equals("mail.QUOTA_EXCEEDED")) {
/* 672 */             ZimbraLog.lmtp.info("rejecting message from=%s,to=%s: overquota", new Object[] { envSender, rcptEmail });
/* 673 */             if (this.config.isPermanentFailureWhenOverQuota()) {
/* 674 */               reply = LmtpReply.PERMANENT_FAILURE_OVER_QUOTA;
/*     */             } else {
/* 676 */               reply = LmtpReply.TEMPORARY_FAILURE_OVER_QUOTA;
/*     */             }
/* 678 */           } else if (e.isReceiversFault()) {
/* 679 */             ZimbraLog.lmtp.info("try again for message from=%s,to=%s", envSender, rcptEmail, e);
/* 680 */             reply = LmtpReply.TEMPORARY_FAILURE;
/*     */           } else {
/* 682 */             ZimbraLog.lmtp.info("rejecting message from=%s,to=%s", envSender, rcptEmail, e);
/* 683 */             reply = LmtpReply.PERMANENT_FAILURE;
/*     */           }
/*     */         } catch (Exception e) {
/* 686 */           reply = LmtpReply.TEMPORARY_FAILURE;
/* 687 */           ZimbraLog.lmtp.warn("try again for message from=%s,to=%s", envSender, rcptEmail, e);
/*     */         } finally {
/* 689 */           if ((rd.action == DeliveryAction.deliver) && (!success))
/*     */           {
/*     */ 
/* 692 */             removeFromDedupeCache(msgId, rd.mbox);
/*     */           }
/* 694 */           recipient.setDeliveryStatus(reply);
/* 695 */           if ((shared) && (rd != null) && (rd.esd)) {
/* 696 */             rd.mbox.endSharedDelivery();
/* 697 */             rd.esd = false;
/*     */           }
/*     */         }
/*     */       }
/*     */       
/*     */ 
/* 703 */       ParsedMessage mimeSource = pmAttachIndex != null ? pmAttachIndex : pmNoAttachIndex;
/* 704 */       MailboxBlob mblob = sharedDeliveryCtxt.getMailboxBlob();
/* 705 */       if ((mblob != null) && (mimeSource != null)) {
/* 706 */         if (bis == null) {
/* 707 */           bis = mimeSource.getBlobInputStream();
/*     */         }
/* 709 */         if (bis != null)
/*     */         {
/*     */           try
/*     */           {
/* 713 */             Blob storedBlob = mblob.getLocalBlob();
/* 714 */             bis.fileMoved(storedBlob.getFile());
/* 715 */             MessageCache.cacheMessage(mblob.getDigest(), mimeSource.getOriginalMessage(), mimeSource.getMimeMessage());
/*     */           } catch (IOException e) {
/* 717 */             ZimbraLog.lmtp.warn("Unable to cache message for " + mblob, e);
/*     */           }
/*     */         }
/*     */       }
/*     */     }
/*     */     finally {
/*     */       Iterator i$;
/*     */       RecipientDetail rd;
/* 725 */       if (shared) {
/* 726 */         for (RecipientDetail rd : rcptMap.values()) {
/* 727 */           if ((rd.esd) && (rd.mbox != null))
/* 728 */             rd.mbox.endSharedDelivery();
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private void deliverMessageToRemoteMailboxes(Blob blob, byte[] data, LmtpEnvelope env) {
/* 735 */     Multimap<String, LmtpAddress> serverToRecipientsMap = env.getRemoteServerToRecipientsMap();
/* 736 */     for (String server : serverToRecipientsMap.keySet()) {
/* 737 */       LmtpClient lmtpClient = null;
/* 738 */       InputStream in = null;
/* 739 */       Collection<LmtpAddress> serverRecipients = serverToRecipientsMap.get(server);
/*     */       try {
/* 741 */         Server serverObj = Provisioning.getInstance().getServerByName(server);
/* 742 */         lmtpClient = new LmtpClient(server, new Integer(serverObj.getAttr("zimbraLmtpBindPort")).intValue());
/* 743 */         in = data == null ? blob.getInputStream() : new ByteArrayInputStream(data);
/* 744 */         boolean success = lmtpClient.sendMessage(in, getRecipientsEmailAddress(serverRecipients), env.getSender().getEmailAddress(), blob.getFile().getName(), Long.valueOf(blob.getRawSize()));
/*     */         
/*     */ 
/*     */ 
/*     */ 
/* 749 */         if (success) {
/* 750 */           setDeliveryStatuses(serverRecipients, LmtpReply.DELIVERY_OK);
/*     */         } else {
/* 752 */           ZimbraLog.lmtp.warn("Unsuccessful remote mail delivery - LMTP response: %s", new Object[] { lmtpClient.getResponse() });
/* 753 */           setDeliveryStatuses(serverRecipients, LmtpReply.TEMPORARY_FAILURE);
/*     */         }
/*     */       } catch (LmtpProtocolException e) {
/* 756 */         ZimbraLog.lmtp.warn("Unsuccessful remote mail delivery - LMTP response: %s", new Object[] { e.getMessage() });
/* 757 */         setDeliveryStatuses(serverRecipients, LmtpReply.TEMPORARY_FAILURE);
/*     */       } catch (Exception e) {
/* 759 */         ZimbraLog.lmtp.warn("Exception delivering remote mail", e);
/* 760 */         setDeliveryStatuses(serverRecipients, LmtpReply.TEMPORARY_FAILURE);
/*     */       } finally {
/* 762 */         ByteUtil.closeStream(in);
/* 763 */         if (lmtpClient != null) {
/* 764 */           lmtpClient.close();
/*     */         }
/*     */       }
/*     */     }
/*     */   }
/*     */   
/*     */   private static String[] getRecipientsEmailAddress(Collection<LmtpAddress> recipients) {
/* 771 */     LmtpAddress[] recipientsArray = (LmtpAddress[])recipients.toArray(new LmtpAddress[recipients.size()]);
/* 772 */     String[] recipientEmailAddrs = new String[recipientsArray.length];
/* 773 */     for (int i = 0; i < recipientsArray.length; i++) {
/* 774 */       recipientEmailAddrs[i] = recipientsArray[i].getEmailAddress();
/*     */     }
/* 776 */     return recipientEmailAddrs;
/*     */   }
/*     */   
/*     */   private void setDeliveryStatuses(Collection<LmtpAddress> recipients, LmtpReply reply) {
/* 780 */     for (LmtpAddress recipient : recipients) {
/* 781 */       recipient.setDeliveryStatus(reply);
/*     */     }
/*     */   }
/*     */ }


/* Location:              /home/mint/zimbrastore.jar!/com/zimbra/cs/lmtpserver/ZimbraLmtpBackend.class
 * Java compiler version: 7 (51.0)
 * JD-Core Version:       0.7.1
 */